package dragon.social.security;

import dragon.social.core.UserDetailsImpl;
import dragon.social.core.handler.AuthFailureHandler;
import dragon.social.core.handler.AuthSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.social.security.SpringSocialConfigurer;


/**
 * @Author: Leo
 * @Date: 2020/2/6 16:14
 */
@Configuration
public class SecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    @Autowired
    AuthenticationProviderImpl authenticationProvider;

    @Autowired
    AuthFailureHandler authFailureHandler;

    @Autowired
    AuthSuccessHandler authSuccessHandler;

    @Autowired
    SpringSocialConfigurer springSocialConfigurer;

    public static final String LOGIN_URL = "/login";
    public static final String LOGIN_PROCESS_URL = "/login/form";





    /**
     * Override this method to configure the {@link HttpSecurity}. Typically subclasses
     * should not invoke this method by calling super as it may override their
     * configuration. The default configuration is:
     *
     * <pre>
     * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
     * </pre>
     *
     * @param http the {@link HttpSecurity} to modify
     * @throws Exception if an error occurs
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {


        //开始配置
        http
                //添加social 配置
                .apply(springSocialConfigurer)
                .and()
                //配置认证请求（URL权限）
                .authorizeRequests()
                //指定页面路径允许所有人访问（不理会是否有登录）
                .antMatchers(LOGIN_URL, "/login/verificateCode", "/test", "/errorpage","/signin","/signup","/error","/user/regist","/user/social").permitAll()
                //指定页面路径只允许拥有MENBER,SUPER_ADMIN角色的用户访问
                .antMatchers("/me").hasAnyRole("MEMBER", "SUPER_ADMIN")
                //除上面以外的其他任何请求都允许登录后的人员访问
                .anyRequest().authenticated()
                //重新获取httpSecurity对象
                .and()
                //切换到设置login
                .formLogin()
                //设置为controller 这里程序默认路径就是登陆页面,可以设置是html页面，也可以是controller
//                .loginPage(LOGIN_URL)
                .loginPage("/authrequire")
                //允许所有人进行登陆
                .permitAll()
                //登陆提交的处理url，默认值为 j_spring_security_check
                .loginProcessingUrl(LOGIN_PROCESS_URL)
                //登陆失败进行转发，这里回到登陆页面，参数error可以告知登陆状态
                .failureForwardUrl("/errorpage")
                //登陆成功的默认url，这里去到个人首页。意思是，直接输入登录url后，点击登录后才跳转到default值。如果是在其他URL跳转到登录界面，登陆后还是会跳到原来的URL
                .defaultSuccessUrl("/index")
                /**
                 * 除了设置default值外，可以使用另一种方式：successHandler，这是登录成功后的处理，做这个处理的话，就会
                 * 导致defaultSuccessUrl失效
                 */
                //登陆成功后的一些处理
//                .successHandler(authSuccessHandler)
                                .successHandler((httpServletRequest, httpServletResponse, authentication) -> {
                    System.out.println("登录成功，需要写入session吗？");
                    boolean b = authentication.getPrincipal() instanceof UserDetailsImpl;
                    System.out.println(b);
                    httpServletResponse.sendRedirect("/spring/index");
                })
                .failureHandler(authFailureHandler)
                //重新获取httpSecurity对象
                .and()
                //切换到设置logout
                .logout()
                //按顺序，第一个是登出的url，security会拦截这个url进行处理，所以登出不需要我们实现，第二个是登出url，logout告知登陆状态
                .logoutUrl("/logout")
                .logoutSuccessUrl("/?logout=true")
                //重新获取httpSecurity对象
                .and()
                //切换到设置rememberMe
                .rememberMe()
                //记住我功能，cookies有限期是一周
                .tokenValiditySeconds(604800)
                //登陆时是否激活记住我功能的参数名字，在登陆页面有展示
                .rememberMeParameter("remember-me")
                //cookies的名字，登陆后可以通过浏览器查看cookies名字
                .rememberMeCookieName("workspace")
                //暂时禁掉跨域限制
                .and().csrf().disable();
    }

    /**
     * Used by the default implementation of {@link #authenticationManager()} to attempt
     * to obtain an {@link AuthenticationManager}. If overridden, the
     * {@link AuthenticationManagerBuilder} should be used to specify the
     * {@link AuthenticationManager}.
     *
     * <p>
     * The {@link #authenticationManagerBean()} method can be used to expose the resulting
     * {@link AuthenticationManager} as a Bean. The {@link #userDetailsServiceBean()} can
     * be used to expose the last populated {@link UserDetailsService} that is created
     * with the {@link AuthenticationManagerBuilder} as a Bean. The
     * {@link UserDetailsService} will also automatically be populated on
     * {@link HttpSecurity#getSharedObject(Class)} for use with other
     * {@link SecurityContextConfigurer} (i.e. RememberMeConfigurer )
     * </p>
     *
     * <p>
     * For example, the following configuration could be used to register in memory
     * authentication that exposes an in memory {@link UserDetailsService}:
     * </p>
     *
     * <pre>
     * &#064;Override
     * protected void configure(AuthenticationManagerBuilder auth) {
     * 	auth
     * 	// enable in memory based authentication with a user named
     * 	// &quot;user&quot; and &quot;admin&quot;
     * 	.inMemoryAuthentication().withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;).and()
     * 			.withUser(&quot;admin&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;, &quot;ADMIN&quot;);
     * }
     *
     * // Expose the UserDetailsService as a Bean
     * &#064;Bean
     * &#064;Override
     * public UserDetailsService userDetailsServiceBean() throws Exception {
     * 	return super.userDetailsServiceBean();
     * }
     *
     * </pre>
     *
     * @param auth the {@link AuthenticationManagerBuilder} to use
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //设置自定义认证器
        auth.authenticationProvider(authenticationProvider);
        System.out.println("配置authenticationprovider");
    }

    /**
     * Override this method to configure {@link WebSecurity}. For example, if you wish to
     * ignore certain requests.
     *  web层面的过滤，这里的ignore，是跳过security
     * @param web
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/login/verificateCode");
    }
}